//
//  QSScanCodeViewController.swift
//  TestSwift
//
//  Created by Song on 2018/4/30.
//  Copyright © 2019 Song. All rights reserved.
//

import UIKit
import AVFoundation

class QSScanCodeViewController: QSBaseViewController, AVCaptureMetadataOutputObjectsDelegate, AVCaptureVideoDataOutputSampleBufferDelegate {
    /** 会话对象 */
    var qs_session: AVCaptureSession?
    /** 预览层类 */
    var qs_previewLayer: AVCaptureVideoPreviewLayer?
    
    /** 子控件 */
    private var qs_scanView: QSScanCodeView!
    
    /** 扫描成功的回调 */
    var qs_scanSuccess: ((QSScanCodeViewController, String) -> ())?
    
    // MARK: - Life Cycle
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.view.backgroundColor = UIColor.black
        
        // 设置二维码扫描
        self.qs_setScanQrCode()
        
        // 创建扫描边框
        qs_scanView = QSScanCodeView.init(frame: self.view.bounds)
        self.view.addSubview(qs_scanView)
    }
    
    init(scanSuccess: @escaping ((QSScanCodeViewController, String) -> ())) {
        super.init(nibName: nil, bundle: nil)
        self.qs_scanSuccess = scanSuccess
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // MARK: - AVCaptureMetadataOutputObjectsDelegate
    /**
     *  设置扫描完成后的提示音
     */
    func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
        // 扫描成功之后播放提示音
        self.qs_playSoundEffect(qs_audioFileName)
        // 如果扫描完成，停止会话
        self.qs_session?.stopRunning()
        // 删除预览图层
        self.qs_previewLayer?.removeFromSuperlayer()
        
        // 设置界面显示扫描结果
        if metadataObjects.count > 0 {
            let obj = metadataObjects[0]
            if let string: String = (obj as AnyObject).stringValue {
                // 扫描成功
                if self.qs_scanSuccess != nil {
                    self.qs_scanSuccess!(self, string)
                }
            }
        }
    }
    
    /**
     *  检测光线强弱
     */
    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        let metadataDict = CMCopyDictionaryOfAttachments(nil, sampleBuffer, kCMAttachmentMode_ShouldPropagate) as? Dictionary<String, Any>
        
        if let exifMetadata = metadataDict![kCGImagePropertyExifDictionary as String] as? Dictionary<String, Any> {
            let brightnessValue = exifMetadata[kCGImagePropertyExifBrightnessValue as String]
            let brightValue = (brightnessValue as! NSNumber).floatValue
            
            // 显示闪光灯按钮
            if let flashLightBtn = self.qs_scanView.qs_flashlightBtn {
                if flashLightBtn.isSelected { return }
                
                flashLightBtn.isHidden = !(brightValue < 0.5)
            }
        }
    }
    
    /**
     *  播放音频文件
     */
    private func qs_playSoundEffect(_ soundName: String) {
        guard let audioFile = Bundle.main.path(forResource: soundName, ofType: nil) else { return }
        
        let fileUrl = NSURL.fileURL(withPath: audioFile)
        
        // 获得系统声音ID
        var soundID: SystemSoundID = 0
        
        /**
         *  in FileUrl:音频文件url
         *  out SystemSoundID:声音id（此函数会将音效文件加入到系统音频服务中并返回一个长整形ID
         */
        AudioServicesCreateSystemSoundID(fileUrl as CFURL, &soundID)
        // 播放音频
        AudioServicesPlaySystemSound(soundID)
    }
    
    // MARK: - Private Methods
    /**
     *  设置二维码扫描
     */
    private func qs_setScanQrCode() {
        // 获取摄像设备
        let device = AVCaptureDevice.default(for: AVMediaType.video)
        
        if device != nil {
            // 判断授权状态
            let authStatus = AVCaptureDevice.authorizationStatus(for: AVMediaType.video)
            
            if authStatus == AVAuthorizationStatus.restricted {
                print("因为系统原因, 无法访问相机")
                return
            } else if authStatus == AVAuthorizationStatus.denied {
                self.qs_showAlertView(title: "警告", message: "请去-> [设置 - 隐私 - 相机 - " + qs_projectName + "] 打开访问开关", otherTitle: "去设置") {
                    // 系统是否大于10
                    if #available(iOS 10.0, *) {
                        let url = URL.init(string: UIApplicationOpenSettingsURLString)
                        UIApplication.shared.openURL(url!)
                    } else {
                        let url = URL.init(string: "prefs:root=privacy")
                        UIApplication.shared.openURL(url!)
                    }
                }
                return
            } else if (authStatus == AVAuthorizationStatus.authorized) { // 用户允许当前应用访问相机
                self.qs_allowScan()
            } else if (authStatus == AVAuthorizationStatus.notDetermined) { // 用户还没有做出选择
                
                AVCaptureDevice.requestAccess(for: AVMediaType.video) { (granted) in
                    DispatchQueue.main.async {
                        if granted {
                            // 用户接受
                            self.qs_allowScan()
                        } else {
                            self.navigationController?.popViewController(animated: true)
                        }
                    }
                }
            } else {
                self.qs_showAlertView(title: "警告", message: "未检测到您的摄像头, 请在真机上测试", cancelTitle: "好的", otherTitle: nil, otherAction: nil)
                return
            }
            
        }
    }
    
    /**
     *  允许扫描
     */
    private func qs_allowScan() {
        guard let device = AVCaptureDevice.default(for: AVMediaType.video) else { return }
        
        // 创建输入流
        let input = try? AVCaptureDeviceInput.init(device: device)
        // 创建输出流
        let output = AVCaptureMetadataOutput.init()
        // 设置代理 在主线程里刷新
        output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
        
        // 光线输出流
        let lightOutput = AVCaptureVideoDataOutput.init()
        lightOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main)
        
        //设置扫描范围(每一个取值0～1，以屏幕右上角为坐标原点)
        output.rectOfInterest = CGRect.init(x: 0.05, y: 0.2, width: 0.7, height: 0.6)
        
        // 初始化链接对象（会话对象
        self.qs_session = AVCaptureSession.init()
        // 高质量采集率
        self.qs_session?.sessionPreset = AVCaptureSession.Preset.high
        
        // 添加会话输入
        if input != nil {
            self.qs_session?.addInput(input!)
        }
        // 添加会话输出
        self.qs_session?.addOutput(output)
        self.qs_session?.addOutput(lightOutput)
        
        // 设置输出数据类型，需要将元数据输出添加到会话后，才能指定元数据类型，否则会报错
        // 设置扫码支持的编码格式(如下设置条形码和二维码兼容)
        output.metadataObjectTypes = [AVMetadataObject.ObjectType.qr, AVMetadataObject.ObjectType.ean13,  AVMetadataObject.ObjectType.ean8, AVMetadataObject.ObjectType.code128]
        
        // 实例化预览图层, 传递_session是为了告诉图层将来显示什么内容
        self.qs_previewLayer = AVCaptureVideoPreviewLayer.init(session: self.qs_session!)
        self.qs_previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
        self.qs_previewLayer?.frame = self.view.layer.bounds
        
        // 将图层插入当前视图
        self.view.layer.insertSublayer(self.qs_previewLayer!, at: 0)
        self.perform(#selector(self.qs_startSessionRun), with: nil, afterDelay: 0.0, inModes: [RunLoopMode.commonModes])
    }
    
    /**
     *  启动会话
     */
    @objc private func qs_startSessionRun() {
        self.qs_session?.startRunning()
    }
    
    /**
     *  显示提示框
     */
    private func qs_showAlertView(title: String, message: String, cancelTitle: String = "取消", otherTitle: String?,  otherAction: (() -> ())?) {
        let alertVC = UIAlertController.init(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
        
        let cancelAct = UIAlertAction.init(title: cancelTitle, style: UIAlertActionStyle.cancel) { (cancel) in
        }
        alertVC.addAction(cancelAct)
        
        if otherTitle != nil {
            let otherAct = UIAlertAction.init(title: otherTitle, style: UIAlertActionStyle.default) { (other) in
                if otherAction != nil {
                    otherAction!()
                }
            }
            alertVC.addAction(otherAct)
        }
        
        self.present(alertVC, animated: true, completion: nil)
    }
}
